import traceback
from flask import Flask, jsonify, request, g, render_template
from supertokens_python import init, SupertokensConfig, InputAppInfo
from supertokens_python.recipe import session, emailpassword
from supertokens_python.recipe.session.syncio import create_new_session, revoke_all_sessions_for_user
from supertokens_python.recipe.emailpassword.syncio import sign_in, sign_up, update_email_or_password, verify_credentials, create_reset_password_token
from supertokens_python.syncio import get_user, sync, list_users_by_account_info
from supertokens_python.recipe.emailpassword.interfaces import (
    SignUpOkResult,
    EmailAlreadyExistsError,
    SignInOkResult,
    WrongCredentialsError,
    UnknownUserIdError,
    CreateResetPasswordOkResult,
    UpdateEmailOrPasswordOkResult,
    PasswordResetTokenInvalidError,
    PasswordPolicyViolationError
)
from supertokens_python.ingredients.emaildelivery import EmailDeliveryIngredient
from supertokens_python.ingredients.emaildelivery.types import EmailDeliveryConfig
from supertokens_python.recipe.emailpassword.types import EmailTemplateVars
from supertokens_python.recipe.emailpassword.interfaces import RecipeInterface
from supertokens_python.recipe.emailpassword.asyncio import send_email as async_send_email
from supertokens_python.recipe.emailpassword.utils import InputOverrideConfig
import asyncio

from supertokens_python.recipe.session.framework.flask import verify_session
from supertokens_python.types import RecipeUserId, User
from supertokens_python.types.base import AccountInfoInput

supertokens_host = "http://47.106.247.233:3567"
app = Flask(__name__)

api_domain = "http://localhost:8000"

class CustomEmailService:
    async def send_email(self, input_: EmailTemplateVars, user_context: dict):
        print("CustomEmailService.send_email was definitely called!")
        if input_.password_reset_link:
            print(f"Password reset link for {input_.user.email}: {input_.password_reset_link}")
        else:
            # Fallback to default email sending if it's not a password reset email
            # Get recipe_implementation dynamically
            recipe_implementation = emailpassword.recipe_implementation
            if recipe_implementation:
                await async_send_email(input_, user_context)

custom_email_delivery = EmailDeliveryConfig(
    service=CustomEmailService()
)

init(
    app_info=InputAppInfo(
        app_name="My SuperTokens App",
        api_domain=api_domain,
        website_domain=api_domain,
        api_base_path="/auth",
    ),
    framework="flask",
    supertokens_config=SupertokensConfig(
        connection_uri=supertokens_host,
        api_key='9FvGdszQkuTWOS4VhjrPPH8LcJ'
    ),
    recipe_list=[
        session.init(),
        emailpassword.init(email_delivery=custom_email_delivery)
    ],
    telemetry=True,
    debug=True,
)

print(f"Actual email delivery service type: {type(emailpassword.recipe.EmailPasswordRecipe.get_instance().email_delivery.ingredient_interface_impl)}")


@app.route("/auth/admin/generate-reset-password-link-for-ui", methods=["POST"])
def generate_reset_password_link_for_ui():
    body = request.json
    email = body.get("email")

    if not email:
        return jsonify({"message": "Email is required"}), 400

    try:
        account_info = AccountInfoInput(email=email)
        users = list_users_by_account_info(tenant_id='public', account_info=account_info)
        if not users:
            return jsonify({"message": "User not found"}), 404
        user_id = users[0].login_methods[0].recipe_user_id

        # Generate the reset password token
        token_result = emailpassword.syncio.create_reset_password_token(tenant_id='public', user_id=user_id.get_as_string(), email=email)
        if isinstance(token_result, UnknownUserIdError):
            return jsonify({"message": "User not found"}), 404

        # Construct the UI link
        reset_ui_link = f"{api_domain}/auth/reset-password?token={token_result.token}&email={email}"
        return jsonify({"message": "Reset password UI link generated", "reset_link": reset_ui_link}), 200

    except Exception as e:
        traceback.print_exc()
        return jsonify({"message": f"An unexpected error occurred: {str(e)}"}), 500


@app.route("/auth/reset-password", methods=["GET"])
def reset_password_page():
    token = request.args.get('token')
    email = request.args.get('email')
    if not token or not email:
        return "Missing token or email", 400
    return render_template('reset_password.html', token=token, email=email)


@app.route("/auth/admin/generate-reset-password-link", methods=["POST"])
def generate_reset_password_link():
    body = request.json
    email = body.get("email")

    if not email:
        return jsonify({"message": "Email is required"}), 400

    try:
        account_info = AccountInfoInput(email=email)
        users = list_users_by_account_info(tenant_id='public', account_info=account_info)
        if not users:
            return jsonify({"message": "User not found"}), 404
        user_id = users[0].login_methods[0].recipe_user_id

        # This will trigger our custom email delivery service to print the link
        print("Attempting to send reset password email...")
        send_email_result = asyncio.run(emailpassword.asyncio.send_reset_password_email(tenant_id='public', user_id=user_id.get_as_string(), email=email))
        print(f"send_reset_password_email result: {send_email_result}")
        return jsonify({"message": "Password reset link generated and printed to server logs."}), 200
    except Exception as e:
        traceback.print_exc()
        return jsonify({"message": f"An unexpected error occurred: {str(e)}"}), 500


@app.route("/auth/reset-password/form", methods=["POST"])
def reset_password_form():
    body = request.json
    token = body.get("token")
    new_password = body.get("newPassword")

    if not token or not new_password:
        return jsonify({"message": "Token and new password are required"}), 400

    try:
        result = emailpassword.syncio.reset_password_using_token(
            tenant_id='public',
            token=token,
            new_password=new_password
        )

        if isinstance(result, UpdateEmailOrPasswordOkResult):
            return jsonify({"message": "Password reset successful"}), 200
        elif isinstance(result, PasswordResetTokenInvalidError):
            return jsonify({"message": "Password reset token invalid"}), 400
        elif isinstance(result, PasswordPolicyViolationError):
            return jsonify({"message": f"Password policy violation: {result.failure_reason}"}), 400
        elif isinstance(result, UnknownUserIdError):
            return jsonify({"message": "Unknown user ID"}), 404
        else:
            return jsonify({"message": "Something went wrong during password reset"}), 500

    except Exception as e:
        traceback.print_exc()
        return jsonify({"message": f"An unexpected error occurred: {str(e)}"}), 500


@app.route("/auth/signup", methods=["POST"])
def signup():
    body = request.json
    username = body.get("username")
    password = body.get("password")

    if not username or not password:
        return jsonify({"message": "username and password are required"}), 400

    result = sign_up(tenant_id='public', email=username, password=password)

    if isinstance(result, SignUpOkResult):
        return jsonify({"message": "Signup successful", "user": {"id": result.user.id, "email": result.user.emails[0]}}), 200
    elif isinstance(result, EmailAlreadyExistsError):
        return jsonify({"message": "Email already exists"}), 400
    else:
        return jsonify({"message": "Something went wrong during sign up"}), 500


@app.route("/auth/login", methods=["POST"])
def login():
    body = request.json
    username = body.get("username")
    password = body.get("password")

    if not username or not password:
        return jsonify({"message": "Username and password are required"}), 400

    result = sign_in(tenant_id='public', email=username, password=password)

    if isinstance(result, SignInOkResult):
        session_container = create_new_session(
            request,
            'public',
            recipe_user_id=RecipeUserId(result.user.id)
        )
        return jsonify({
            "message": "Login successful", 
            "userId": result.user.id,
            "accessToken": session_container.get_access_token()
        }), 200
    elif isinstance(result, WrongCredentialsError):
        return jsonify({"message": "Invalid username or password"}), 401
    else:
        return jsonify({"message": "Something went wrong"}), 500


@app.route("/auth/change-password", methods=["POST"])
@verify_session()
def change_password():
    body = request.json
    old_password = body.get("old_password")
    new_password = body.get("new_password")
    session_ = g.supertokens
    user_id = session_.get_user_id()

    if not old_password or not new_password:
        return jsonify({"message": "Old and new passwords are required"}), 400

    try:
        user_info = get_user(user_id)
        login_method = next(
            (
                m
                for m in user_info.login_methods
                if m.recipe_id == "emailpassword"
            ),
            None,
        )
        if login_method is None:
            return jsonify({"message": "User not found"}), 404

        result = verify_credentials(tenant_id='public', email=login_method.email, password=old_password)
        if isinstance(result, WrongCredentialsError):
            return jsonify({"message": "Invalid old password"}), 401

        update_result = update_email_or_password(recipe_user_id=RecipeUserId(user_id), email=login_method.email, password=new_password)
        print(f"Update password result: {update_result}")
        if isinstance(update_result, UnknownUserIdError):
            return jsonify({"message": "Unknown user ID"}), 404

        revoke_all_sessions_for_user(user_id)
        sync(session_.revoke_session())
        return jsonify({"message": "Password changed successfully"}), 200

    except Exception as e:
        return jsonify({"message": f"An unexpected error occurred: {str(e)}"}), 500


@app.route("/auth/logout", methods=["POST"])
@verify_session()
def logout():
    try:
        session_ = g.supertokens
        session_.revoke_session()
        return jsonify({"message": "Logged out successfully"}), 200
    except Exception as e:
        print(f"Error during logout: {e}")
        traceback.print_exc()
        return jsonify({"message": "Logout failed"}), 500


@app.route("/protected", methods=["GET"])
@verify_session()
def protected_route():
    try:
        session_ = g.supertokens
        user_id = session_.get_user_id()
        return jsonify({"message": f"Hello, user {user_id}. This is a protected route."}), 200
    except Exception as e:
        print(f"Error in protected route: {e}")
        traceback.print_exc()
        return jsonify({"message": "Unauthorized"}), 401


@app.route("/")
def index():
    return "Flask + SuperTokens is working!"


if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8000, debug=True)
